/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// presto_protocol.prolog.cpp
//

{{#.}}
{{#comment}}
{{comment}}
{{/comment}}
{{/.}}

#include <iostream>

#include "presto_cpp/presto_protocol/connector/arrow_flight/presto_protocol_arrow_flight.h"
using namespace std::string_literals;

// ArrowTransactionHandle is special since
// the corresponding class in Java is an enum.

namespace facebook::presto::protocol::arrow_flight {

void to_json(json& j, const ArrowTransactionHandle& p) {
  j = json::array();
  j.push_back(p._type);
  j.push_back(p.instance);
}

void from_json(const json& j, ArrowTransactionHandle& p) {
  j[0].get_to(p._type);
  j[1].get_to(p.instance);
}
} // namespace facebook::presto::protocol::arrow_flight
{{#.}}
{{#cinc}}
{{&cinc}}
{{/cinc}}
{{^cinc}}
{{#struct}}
namespace facebook::presto::protocol::arrow_flight {
    {{#super_class}}
    {{&class_name}}::{{&class_name}}() noexcept {
        _type = "{{json_key}}";
    }
    {{/super_class}}

    void to_json(json& j, const {{&class_name}}& p) {
        j = json::object();
        {{#super_class}}
        j["@type"] = "{{&json_key}}";
        {{/super_class}}
        {{#fields}}
        to_json_key(j, "{{&field_name}}", p.{{field_name}}, "{{&class_name}}", "{{&field_text}}", "{{&field_name}}");
        {{/fields}}
    }

    void from_json(const json& j, {{&class_name}}& p) {
        {{#super_class}}
        p._type = j["@type"];
        {{/super_class}}
        {{#fields}}
        from_json_key(j, "{{&field_name}}", p.{{field_name}}, "{{&class_name}}", "{{&field_text}}", "{{&field_name}}");
        {{/fields}}
    }
}
{{/struct}}
{{#enum}}
namespace facebook::presto::protocol::arrow_flight {
    //Loosely copied this here from NLOHMANN_JSON_SERIALIZE_ENUM()

    // NOLINTNEXTLINE: cppcoreguidelines-avoid-c-arrays
    static const std::pair<{{&class_name}}, json>
    {{&class_name}}_enum_table[] = {                // NOLINT: cert-err58-cpp
        {{#elements}}
        { {{&class_name}}::{{&element}}, "{{&element}}" }{{^_last}},{{/_last}}
        {{/elements}}
    };
    void to_json(json& j, const {{&class_name}}& e)
    {
        static_assert(std::is_enum<{{&class_name}}>::value, "{{&class_name}} must be an enum!");
        const auto* it = std::find_if(std::begin({{&class_name}}_enum_table), std::end({{&class_name}}_enum_table),
                               [e](const std::pair<{{&class_name}}, json>& ej_pair) -> bool
        {
            return ej_pair.first == e;
        });
        j = ((it != std::end({{&class_name}}_enum_table)) ? it : std::begin({{&class_name}}_enum_table))->second;
    }
    void from_json(const json& j, {{&class_name}}& e)
    {
        static_assert(std::is_enum<{{&class_name}}>::value, "{{&class_name}} must be an enum!");
        const auto* it = std::find_if(std::begin({{&class_name}}_enum_table), std::end({{&class_name}}_enum_table),
                               [&j](const std::pair<{{&class_name}}, json>& ej_pair) -> bool
        {
            return ej_pair.second == j;
        });
        e = ((it != std::end({{&class_name}}_enum_table)) ? it : std::begin({{&class_name}}_enum_table))->first;
    }
}
{{/enum}}
{{#abstract}}
namespace facebook::presto::protocol::arrow_flight {
    void to_json(json& j, const std::shared_ptr<{{&class_name}}>& p) {
        if ( p == nullptr ) {
            return;
        }
        String type = p->_type;

        {{#subclasses}}
        if ( type == "{{&key}}" ) {
            j = *std::static_pointer_cast<{{&type}}>(p);
            return;
        }
        {{/subclasses}}

        throw TypeError(type + " no abstract type {{&class_name}} {{&key}}");
    }

    void from_json(const json& j, std::shared_ptr<{{&class_name}}>& p) {
        String type;
        try {
          type = p->getSubclassKey(j);
        } catch (json::parse_error &e) {
            throw ParseError(std::string(e.what()) + " {{&class_name}} {{&key}} {{&class_name}}");
        }

        {{#subclasses}}
        if ( type == "{{&key}}" ) {
            std::shared_ptr<{{&type}}> k = std::make_shared<{{&type}}>();
            j.get_to(*k);
            p = std::static_pointer_cast<{{&class_name}}>(k);
            return;
        }
        {{/subclasses}}

        throw TypeError(type +  " no abstract type {{&class_name}} {{&key}}");
    }
}
{{/abstract}}
{{/cinc}}
{{/.}}
